import numpy as np
from matplotlib import pyplot as plt
from skimage.metrics import mean_squared_error
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
import pandas as pd
plt.style.use('ggplot')

# N0 属性	数据类型	  字段描述x
# 1	CRIM	Float	  城镇人均犯罪率
# 2	ZN	    Float	  占地面积超过2.5万平方英尺的住宅用地比例
# 3	INDUS	Float	  城镇非零售业务地区的比例
# 4	CHAS	Integer	  查尔斯河虚拟变量 (= 1 如果土地在河边；否则是0)
# 5	NOX	    Float	  一氧化氮浓度（每1000万份）
# 6	RM	    Float	  平均每居民房数
# 7	AGE	    Float	  在1940年之前建成的所有者占用单位的比例
# 8	DIS	    Float	  与五个波士顿就业中心的加权距离
# 9	RAD	    Integer	  辐射状公路的可达性指数
# 10 TAX	Float	  每10,000美元的全额物业税率
# 11 PTRATIO Float	  城镇师生比例
# 12 B	    Float	  1000（Bk - 0.63）^ 2其中Bk是城镇黑人的比例
# 13 LSTAT	Float	  人口中地位较低人群的百分数

# 14 MEDV	Float	  特征：（目标变量/类别属性）以1000美元计算的自有住房的中位数

"""
获取boston数据集
特征描述通过.DESCR获取，特征名称集通过feature_names获取，特征数据集描述通过X.inf0()获取
"""
############################################################################################
# 从 OpenML 获取波士顿房价数据集
boston = fetch_openml(name='boston', version=1, as_frame=True, parser='auto')
X, y = boston['data'], boston['target']

# 将category类型转为int类型，不然后面会报错
X.loc[:, 'CHAS'] = X['CHAS'].astype(int)
X.loc[:, 'RAD'] = X['RAD'].astype(int)

# 数据集类型转化为pandas.core.frame.DataFrame
data = pd.DataFrame(X, columns=boston.feature_names)

# print(data.head())            可以输出带有列名的表格
# print(boston.DESCR)           对每个特征进行描述
# print(X.info())  = data.info  得到每一列名称，多少个数是否有缺失值，数据类型
# print(X.shape)   = data.shape =  (506, 13)      统计样本数量以及特征数量
# 获取特征名称 feature_names = boston.feature_names
############################################################################################


"""
相关性检验
看看各个特征中是否有相关性，判断一下哪种模型比较合适
"""
#############################################################################################
# Seaborn 是一个基于Matplotlib的数据可视化库，用于制作有吸引力和有信息量的统计图形
import seaborn as sns

# 使用join函数，将标签值加入到数据集中,可以显示每个特征和标签之间的相关系数
y_df = pd.DataFrame(y, columns=['MEDV'])

# 使用join函数将y_df添加到data中
data_test = data.join(y_df)

# plt.figure(figsize=(12, 8))
# sns.heatmap(data_test.corr(), annot=True, fmt='.2f', cmap='PuBu')

# plt.show()
# 使用Seaborn库中的heatmap函数来创建一个热力图，用来计算各个数值列之间的相关系数
# data.corr() 这是pandas DataFrame的方法，用于计算数据集中各个数值列的相关系数，结果是一个系数矩阵
# annot=True  这个参数指定是否在热力图的每个单元格中显示相关系数的值，True会显示
# fmt='.2f'   这个参数指定单元格中数值的格式化字符串，保留两位小数
# cmap='PuBu' 这个参数指定热力图使用的颜色映射（color map）
# 此外，sns.heatmap函数还有一些其他的参数可以自定义热力图的外观
# 包括x轴，y轴、单元格之间的线条宽度和颜色

# 显示每个特征和标签的相关系数
# print(data_test.corr()['MEDV'].sort_values())
#############################################################################################


"""
多变量研究
尝试了解特征值和标签值、特征值和特征值之间的关系
"""
#############################################################################################
# plt.figure(figsize=(10, 6))
# sns.pairplot(data_test[["LSTAT", "RM", "PTRATIO", "MEDV"]])

# plt.show()
# 使用Seaborn库中的pairplot函数来创建一个成对关系图
# sns.pairplot会为每一对特征生成一个散点图，以及每个特征自己的直方图或核密度估计图
# data_test[["LSTAT", "RM", "PTRATIO", "MEDV"]] 选取了四列
# 此外， kind指定图表的类型，可以是scatter（散点图），reg（线性回归图），redis（残差图）
#############################################################################################


"""
建立线性回归模型
"""
#############################################################################################
# 划分训练集，测试集，自动化打乱似乎要比手动打乱效果要好，不知道为什么
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=888)

# 特征工程 step1 数据标准化处理
trans_normal = StandardScaler()
X_train = trans_normal.fit_transform(X_train)

# 建立线性回归模型并使用训练集进行训练
model = LinearRegression()
model.fit(X_train, y_train)

# 对测试集进行标准化转换
X_test = trans_normal.transform(X_test)

# 对测试集进行预测
y_pre = model.predict(X_test)

# model.score 用于评估线性回归模型的性能，计算并返回模型在给定测试数据X_test和标签值y_test上的R2分数（0-1）
print('SCORE:{:.4f}'.format(model.score(X_test, y_test)))

# 获得预测值和实际值之间的均方误差
print('MSE:{:.4f}'.format(np.sqrt(mean_squared_error(y_test, y_pre))))

# X_train, y_train, X_test, y_test = X[:354], y[:354], X[354:], y[354:]
# 洗牌操作，打乱顺序，体现数据是独立的，确保模型不会对数据的特定顺序产生依赖
# shuffle_index = np.random.permutation(354)
# X_train, y_train = X_train.iloc[shuffle_index], y_train.iloc[shuffle_index]

# # 获得参数值
# coef = model.coef_
# # 获得截距值
# intercept = model.intercept_

# 获得参数矩阵
# df_coef = pd.DataFrame()
# df_coef['Title'] = boston.feature_names    这是DataFrame中添加列的一种方式 直接赋值
# df_coef['Coefs'] = coef
# print(df_coef)
#############################################################################################

"""
结果可视化
"""
#############################################################################################
hos_pre = pd.DataFrame()
hos_pre['Predict'] = y_pre

# 注意由于y_test已经被打乱，前面的行号索引并不是从0开始,因此需要重置索引
y_adjust = y_test.reset_index(drop=True)

# hos_pre['Truth'] = y_adjust
# hos_pre.plot(figsize=(12, 8))
# plt.show()

# # #评价模型
# plt.scatter(y_test, y_pre, label='y')
# plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'k--', lw=4, label='predicted')
# # 显示图例
# plt.legend()
# plt.show()
#############################################################################################

"""
进一步模型探索和改进
plan1：使用相关系数的特征进行模型重构
"""
#############################################################################################
# 前面热力图中，已经做了每个特征和标签值之间的相关系数，可以使用相关系数大于5的特征进行模型重构
medv_corr = data_test.corr()['MEDV'].sort_values()

# 筛选出相关系数大于 0.5 的项
high_corr = medv_corr[abs(medv_corr) > 0.5]

# 得到新的数据集合
X2 = X[list(high_corr.index.drop('MEDV'))]

# 划分训练集、测试集
X2_train, X2_test, y2_train, y2_test = train_test_split(X2, y, test_size=0.2, random_state=888)

# 特征工程 step1 数据标准化处理

X2_train = trans_normal.fit_transform(X2_train)

# 建立线性回归模型并使用训练集进行训练
model2 = LinearRegression()
model2.fit(X2_train, y2_train)

# 对测试集进行标准化转换
X2_test = trans_normal.transform(X2_test)

# 对测试集进行预测
y2_pre = model2.predict(X2_test)

# model.score 用于评估线性回归模型的性能，计算并返回模型在给定测试数据X_test和标签值y_test上的R2分数（0-1）
print('SCORE:{:.4f}'.format(model2.score(X2_test, y2_test)))

# 获得预测值和实际值之间的均方误差
print('MSE:{:.4f}'.format(np.sqrt(mean_squared_error(y2_test, y2_pre))))
#############################################################################################



